package com.netx.cubigraf.entities;
import com.netx.generics.sql.Row;
import com.netx.generics.sql.Table;
import com.netx.generics.time.Moment;
import com.netx.generics.util.Strings;
import com.netx.data.Connection;
import com.netx.data.DatabaseException;
import com.netx.data.Entity;
import com.netx.data.RestrictionException;
import com.netx.data.ValueList;
import com.netx.ebs.User;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;


public class Servicos extends Entity {

	public Servicos() {
		super();
	}

	public long criarServico(String nome, long cdgCliente, long cdgDesenhador) throws DatabaseException {
		ValueList values = new ValueList();
		values.addValue("referencia", _proximaReferencia());
		values.addValue("nome", nome);
		values.addValue("cdg_cliente", cdgCliente);
		values.addValue("cdg_desenhador", cdgDesenhador);
		values.addValue("data_abertura", new Moment().getDate());
		values.addValue("estado", 'a');
		return insert(values);
	}

	public long criarServico(long cdgOriginal, long cdgDesenhador, boolean keepRelation) throws DatabaseException {
		ValueList values = new ValueList();
		values.addValue("data_abertura", new Moment().getDate());
		values.addValue("referencia", _proximaReferencia());
		values.addValue("estado", "a");
		if(keepRelation) {
			values.addValue("cdg_original", cdgOriginal);
		}
		values.addValue("cdg_desenhador", cdgDesenhador);
		Row data = select("nome, cdg_cliente, tipo, tamanho, alteracao_tamanho, largura, altura, numero_cores, numero_linhas, camada, branco_highlight, base, floco, num_impressoes, observacoes", cdgOriginal);
		// add the 15 fields selected:
		for(int i=1; i<=15; i++) {
			values.addValue(data.getColumnName(i), data.getString(i));
		}
		return insert(values);
	}

	public void guardarServico(long cdgServico, ValueList values) throws RestrictionException, DatabaseException {
		checkRestriction(cdgServico, "estado", "a");
		update(cdgServico, values);
	}
	
	public void terminarServico(long cdgServico) throws RestrictionException, DatabaseException {
		checkRestriction(cdgServico, "estado", "a");
		ValueList values = new ValueList();
		values.addValue("data_print", new Moment().getDate());
		values.addValue("estado", "t");
		Connection c = getConnection();
		synchronized(c) {
			startTransaction();
			update(cdgServico, values);
			commit();
		}
	}

	public void corrigirServico(long cdgServico, ValueList values) throws RestrictionException, DatabaseException {
		checkRestriction(cdgServico, "estado", "t");
		update(cdgServico, values);
	}

	public void aprecarServico(long cdgServico, double preco, boolean facturar) throws RestrictionException, DatabaseException {
		checkRestriction(cdgServico, "estado", "t");
		Connection c = getConnection();
		Facturas facturas = Entities.getFacturas(c);
		NaoFacturados nf = Entities.getNaoFacturados(c);
		FichasEntrega fe = Entities.getFichasEntrega(c);
		synchronized(c) {
			startTransaction();
			ValueList values = new ValueList();
			values.addValue("estado", "p");
			update(cdgServico, values);
			long cdgCliente = select("cdg_cliente", cdgServico).getLong(1);
			if(facturar) {
				facturas.adicionarLinha(cdgServico, cdgCliente, preco);
			}
			else {
				nf.adicionarLinha(cdgServico, preco);
			}
			fe.adicionarLinha(cdgServico, cdgCliente, facturar);
			commit();
		}
	}
	
	//TODO sera necessario este estado?
	// for Facturas:
	// (assumes a transaction has been started)
	void facturarServico(long cdgServico) throws DatabaseException {
		ValueList values = new ValueList();
		values.addValue("estado", "f");
		update(cdgServico, values);
	}
	
	public void reverterCorrentes(long cdgServico) throws RestrictionException, DatabaseException {
		_reverter(cdgServico, "a");
	}

	public void reverterAprecar(long cdgServico) throws RestrictionException, DatabaseException {
		_reverter(cdgServico, "t");
	}

	private void _reverter(long cdgServico, String estado) throws RestrictionException, DatabaseException {
		checkRestriction(cdgServico, "estado", "p");
		Connection c = getConnection();
		Facturas facturas = Entities.getFacturas(c);
		NaoFacturados nf = Entities.getNaoFacturados(c);
		FichasEntrega fe = Entities.getFichasEntrega(c);
		synchronized(c) {
			startTransaction();
			ValueList values = new ValueList();
			values.addValue("estado", estado);
			update(cdgServico, values);
			if(facturas.existeLinha(cdgServico)) {
				facturas.removerLinha(cdgServico);
			}
			else {
				nf.removerLinha(cdgServico);
			}
			fe.removerLinha(cdgServico);
			commit();
		}
	}

	public List<Long> verHistoricoPosterior(long servicoId) throws DatabaseException {
		List<Long> history = new ArrayList<Long>();
		_verPosteriores(servicoId, history);
		return history;
	}

	private void _verPosteriores(long servicoId, List<Long> history) throws DatabaseException {
		Table t = select("cdg_servico", "cdg_original="+servicoId+" ORDER BY cdg_servico");
		if(!t.isEmpty()) {
			// Add codes:
			for(int i=0; i<t.getRowCount(); i++) {
				if(t.getLong(1, i) != 0) {
					history.add(t.getLong(1, i));
				}
			}
			// Iterate:
			for(int i=0; i<t.getRowCount(); i++) {
				if(t.getLong(1, i) != 0) {
					_verPosteriores(t.getLong(1, i), history);
				}
			}
		}
	}
	
	public List<Long> verHistoricoAnterior(long servicoId) throws DatabaseException {
		List<Long> history = new ArrayList<Long>();
		_verAnteriores(servicoId, history);
		Collections.reverse(history);
		return history;
	}

	private void _verAnteriores(long servicoId, List<Long> history) throws DatabaseException {
		Row r = select("cdg_original", servicoId);
		if(r != null) {
			long childId = r.getLong(1);
			if(childId != 0) {
				history.add(childId);
				_verAnteriores(childId, history);
			}
		}
	}

	public User verDesenhador(long servicoId) throws DatabaseException {
		long userId = select("cdg_desenhador", servicoId).getLong(1);
		return Entities.getUsers(getConnection()).find(userId);
	}

	public char verEstado(long servicoId) throws DatabaseException {
		Row r = select("estado", servicoId);
		if(r == null) {
			return '\0';
		}
		else {
			return r.getChar(1);
		}
	}

	// 1:  cdg_servico
	// 2:  referencia
	// 3:  nome
	// 4:  data de abertura
	// 5:  tipo
	// 6:  tamanho
	// 7:  alteracao de tamanho
	// 8:  largura
	// 9:  altura
	// 10: numero de cores
	// 11: numero de linhas
	// 12: camada
	// 13: branco highlight
	// 14: base
	// 15: codigo do original
	// 16: codigo da unidade de saida
	// 17: codigo do cliente
	// 18: nome do cliente
	// 19: codigo do desenhador
	// 20: nome do desenhador
	// 21: numero de impressoes
	// 22: floco
	// 23: observacoes
	public Row verServicoParaEditar(long cdgServico) throws DatabaseException {
		return getConnection().select("s.cdg_servico, s.referencia, s.nome, s.data_abertura, s.tipo, s.tamanho, s.alteracao_tamanho, s.largura, s.altura, s.numero_linhas, s.numero_cores, s.camada, s.branco_highlight, s.base, s.cdg_original, s.cdg_unidade_saida, s.cdg_cliente, c.nome, s.cdg_desenhador, u.name, s.num_impressoes, s.floco, s.observacoes FROM servicos s, clientes c, users u WHERE s.cdg_servico="+cdgServico+" AND s.cdg_cliente=c.cdg_cliente AND u.user_id=s.cdg_desenhador").getRow(0);
	}

	// 1:  cdg_servico
	// 2:  referencia
	// 3:  nome
	// 4:  data de abertura
	// 5:  data de print
	// 6:  tipo
	// 7:  tamanho
	// 8:  alteracao de tamanho
	// 9:  largura
	// 10: altura
	// 11: numero de cores
	// 12: numero de linhas
	// 13: camada
	// 14: branco highlight
	// 15: base
	// 16: estado
	// 17: codigo do original
	// 18: nome do cliente
	// 19: nome do desenhador
	// 20: nome da unidade de saida
	// 21: numero de impressoes
	// 22: floco
	// 23: observacoes
	public Row verServico(long cdgServico) throws DatabaseException {
		Table t = getConnection().select("s.cdg_servico, s.referencia, s.nome, s.data_abertura, s.data_print, s.tipo, s.tamanho, s.alteracao_tamanho, s.largura, s.altura, s.numero_cores, s.numero_linhas, s.camada, s.branco_highlight, s.base, s.estado, s.cdg_original, c.nome, u.name, us.nome, s.num_impressoes, s.floco, s.observacoes FROM servicos s, clientes c, users u, unidades_saida us WHERE s.cdg_servico="+cdgServico+" AND s.cdg_cliente=c.cdg_cliente AND s.cdg_desenhador=u.user_id AND s.cdg_unidade_saida=us.cdg_unidade_saida");
		if(t.isEmpty()) {
			return null;
		}
		else {
			return t.getRow(0);
		}
	}
	
	public double verPrecoServico(long cdgServico) throws DatabaseException {
		Table t = getConnection().select("preco FROM linhas_factura WHERE cdg_servico="+cdgServico);
		if(t.isEmpty()) {
			// tentar encontrar o preco em nao_facturados:
			t = getConnection().select("preco FROM nao_facturados WHERE cdg_servico="+cdgServico);
			if(t.isEmpty()) {
				return 0.0F;
			}
		}
		return t.getDouble(1, 0);
	}

	// procuras:
	private final static String queryFields = "s.cdg_servico, s.nome, s.data_abertura, s.referencia, s.largura, s.altura, s.numero_cores, c.nome, u.name";
	private final static String queryTables = "servicos s, clientes c, users u";
	private final static String queryWhere  = "c.cdg_cliente=s.cdg_cliente AND u.user_id=s.cdg_desenhador";
		
	public Table procurarUltimos(int qtos) throws DatabaseException {
		return getConnection().select(queryFields+" FROM "+queryTables+" WHERE "+queryWhere+" AND s.estado!='a' ORDER BY cdg_servico DESC LIMIT 0, "+qtos);
	}

	public Table procurarServicos(long cdgCliente, String nome, long cdgDesenhador, String data1, String data2) throws DatabaseException {
		StringBuilder query = new StringBuilder(queryFields+" FROM "+queryTables+" WHERE "+queryWhere);
		query.append(" AND s.estado!='a'");
		if(cdgCliente != 0) {
			query.append(" AND s.cdg_cliente="+cdgCliente);
		}
		if(nome != null) {
			query.append(" AND s.nome LIKE \"%"+Strings.addSlashes(nome)+"%\"");
		}
		if(cdgDesenhador != 0) {
			query.append(" AND s.cdg_desenhador="+cdgDesenhador);
		}
		if(data1 != null) {
			if(data2 == null) {
				query.append(" AND s.data_abertura >= \""+data1+"\"");
			}
			else {
				query.append(" AND s.data_abertura BETWEEN \""+data1+"\" AND \""+data2+"\"");
			}
		}
		query.append(" ORDER BY s.data_abertura, s.referencia");
		return getConnection().select(query.toString());
	}

	public Table procurarServicosParaAprecar() throws DatabaseException {
		return getConnection().select(queryFields+" FROM "+queryTables+" WHERE "+queryWhere+" AND s.estado='t' ORDER BY cdg_servico ASC");
	}

	public Table procurarServicosParaFacturar() throws DatabaseException {
		return getConnection().select(queryFields+" FROM "+queryTables+", linhas_factura lf, facturas f WHERE "+queryWhere+" AND s.estado='p' AND lf.cdg_servico=s.cdg_servico AND lf.cdg_factura=f.cdg_factura AND f.estado='a' AND s.cdg_servico > 26000 ORDER BY s.cdg_servico DESC");
	}

	public Table procurarServicosPorFora() throws DatabaseException {
		return getConnection().select(queryFields+" FROM servicos s, nao_facturados nf, clientes c, users u where "+queryWhere+" and nf.cdg_servico=s.cdg_servico and s.cdg_servico > 22000 and curdate()-nf.data_aprecado < 15 order by s.cdg_servico desc");
	}

	public Table procurarServicosParaEntregar() throws DatabaseException {
		return getConnection().select(queryFields+" FROM "+queryTables+" WHERE "+queryWhere+" AND s.estado='f' ORDER BY cdg_servico ASC");
	}
	
	public int proximoParaAprecar(long cdgServico) throws DatabaseException {
		// returns 0 if there are none
		return select("MIN(cdg_servico)", "estado='t' AND cdg_servico>"+cdgServico).getInt(1, 0);
	}

	public Referencia verReferencia(long cdgServico) throws DatabaseException {
		Row ref = select("data_abertura, referencia", cdgServico);
		return new Referencia(ref.getDate(1), ref.getInt(2));
	}

	private int _proximaReferencia() throws DatabaseException {
		Table lastId = select("MAX(cdg_servico)");
		// (at least) MySQL returns '0' for MAX queries, even if there are no elements inserted
		if(lastId.isEmpty() || lastId.getLong(1, 0) == 0) {
			return 1;
		}
		else {
			Row row = select("referencia, data_abertura", lastId.getLong(1, 0));
			if(row.getDate("data_abertura").getYear() < new Moment().getDate().getYear()) {
				return 1;
			}
			else {
				int ref = row.getInt("referencia");
				return (ref == 9999) ? 1 : ref+1;
			}
		}
	}
}
